﻿using System;
using System.Collections.Generic;
using System.Data;
using viNews.Classes;
using viNews.Providers;
using visinia.SmartEngine.Cache;
using visinia.SmartEngine.Modules;

namespace viNews.Controllers
{
    /// <summary>
    /// The news data filter controller
    /// used for filtering the news data like by date, by category, by tag
    /// </summary>
    public class DataFilterController
    {
        static bool bEventRegistered = false;
        public static void DeleteDataFilters(object Source, EventArgs e)
        {
            DataFilter sourcefilter = GetFilter((int)Source);
            if (sourcefilter != null)
                DeleteFilter(sourcefilter.FilterId);
        }

        public enum FILTER
        {
            LATEST_BY_DATE = 1,
            LATEST_BY_CATEGORY = 2,
            LATEST_BY_TAG = 3,
            NEWS_LIST = 4,
        }

        #region ACCESS
        /// <summary>
        /// Gets all the filters
        /// </summary>
        /// <returns>List of all filters</returns>
        public static List<DataFilter> GetFilters()
        {
            List<DataFilter> list = new List<DataFilter>();
            if (DataCache.GetCache("newsFiltersAll") == null)
            {
                list = FillFilters(viNewsDataProvider.Provider.GetFilters());
                DataCache.SetCache("newsFiltersAll", list);
                return list;
            }
            else
                return (List<DataFilter>)DataCache.GetCache("newsFiltersAll");
        }

        /// <summary>
        /// Gets the specified filter
        /// </summary>
        /// <param name="ModuleId">The module id whose filter to return</param>
        /// <returns>The filter</returns>
        public static DataFilter GetFilter(int ModuleId)
        {
            string key = string.Format("newsFilter_{0}", ModuleId);
            DataFilter filter = (DataFilter)DataCache.GetCache(key);
            if (filter == null)
            {
                filter = GetFilters().Find(p => p.ModuleId == ModuleId);
                DataCache.SetCache(key, filter);
            }
            return filter;
        }
        #endregion

        #region DATA
        public static void AddFilter(DataFilter filter)
        {
            viNewsDataProvider.Provider.AddFilter(Convert.ToInt32(filter.FilterType), filter.ModuleId, filter.Data, filter.ItemsCount);
            DataCache.DeleteCacheMultiple("newsFilter");
        }

        public static void UpdateFilter(DataFilter filter)
        {
            viNewsDataProvider.Provider.UpdateFilter(filter.FilterId, Convert.ToInt32(filter.FilterType), filter.ModuleId, filter.Data, filter.ItemsCount);
            DataCache.DeleteCacheMultiple("newsFilter");
        }

        public static void DeleteFilter(int FilterId)
        {
            viNewsDataProvider.Provider.DeleteFilter(FilterId);
            DataCache.DeleteCacheMultiple("newsFilter");
        }
        #endregion

        #region HELPERS
        private static DataFilter FillFilter(IDataReader dr)
        {
            while (dr.Read())
            {
                return ActualRead(dr, true);
            }
            return null;
        }
        private static List<DataFilter> FillFilters(IDataReader dr)
        {
            List<DataFilter> list = new List<DataFilter>();
            while (dr.Read())
            {
                list.Add(ActualRead(dr, false));
            }
            dr.Close();
            return list;
        }
        private static DataFilter ActualRead(IDataReader dr, bool CloseDataReader)
        {
            DataFilter filter = new DataFilter();
            if (dr["FilterId"] != Convert.DBNull)
                filter.FilterId = (int)dr["FilterId"];
            if (dr["FilterType"] != Convert.DBNull)
                filter.FilterType = (FILTER)dr["FilterType"];
            if (dr["ModuleId"] != Convert.DBNull)
                filter.ModuleId = (int)dr["ModuleId"];
            if (dr["Data"] != Convert.DBNull)
                filter.Data = (string)dr["Data"];
            if (dr["ItemsCount"] != Convert.DBNull)
                filter.ItemsCount = (int)dr["ItemsCount"];
            if(CloseDataReader)
                dr.Close();
            return filter;
        }
        #endregion

        #region DATA ACCESS HELPERS

        private const string KEY_FILTERED_NEWS = "newsFilterData_{0}";
        private const int FILTERED_NEWS_EXPIRY = 20;

        /// <summary>
        /// The actual method to filter the news data
        /// </summary>
        /// <param name="ModuleId"></param>
        /// <returns></returns>
        public static List<News> GetFilteredData(int ModuleId)
        {
            // at the time i am finding no other place for this event registration 
            if (!bEventRegistered)
            {
                ModuleController.ModuleDeleted += new EventHandler<EventArgs>(DeleteDataFilters);
                bEventRegistered = true;
            }
            DataFilter filter = GetFilter(ModuleId);
            if (filter == null)
                return null;
            if (filter.ItemsCount == 0)
                return null;
            string key = string.Format(KEY_FILTERED_NEWS, filter.FilterId);
            List<News> list = (List<News>)DataCache.GetCache(key);
            if (list != null)
                return list;
            else
            {
                int temp = 0;
                switch (GetFilter(ModuleId).FilterType)
                {
                    case FILTER.LATEST_BY_DATE:
                        list = NewsAccessHelper.GetNews(filter.ItemsCount);
                        DataCache.SetCache(key, list, new TimeSpan(0, FILTERED_NEWS_EXPIRY, 0));
                        return list;
                    case FILTER.LATEST_BY_CATEGORY:
                        if (Int32.TryParse(filter.Data, out temp))
                        {
                            Category category = CategoryController.GetCategory(temp);
                            if (category == null)//category does'nt exist
                                return null;
                            list = NewsAccessHelper.GetNews(category, filter.ItemsCount);
                            DataCache.SetCache(key, list, new TimeSpan(0, FILTERED_NEWS_EXPIRY, 0));
                            return list;
                        }
                        return null;
                    case FILTER.LATEST_BY_TAG:
                        if (Int32.TryParse(filter.Data, out temp))
                        {
                            Tag tag = TagController.GetTags().Find(p => p.TagId == temp);
                            if (tag == null)// tag does'nt exist
                                return null;
                            list = NewsAccessHelper.GetNews(tag, filter.ItemsCount);
                            DataCache.SetCache(key, list, new TimeSpan(0, FILTERED_NEWS_EXPIRY, 0));
                            return list;
                        }
                        return null;
                    case FILTER.NEWS_LIST:
                        if (string.IsNullOrEmpty(filter.Data))
                            return null;
                        list = NewsAccessHelper.GetNews(filter.Data, filter.ItemsCount);
                        DataCache.SetCache(key, list, new TimeSpan(0, FILTERED_NEWS_EXPIRY, 0));
                        return list;
                }
            }
            return null;
        }

        public static int GetFilteredDataPagesCount(int ModuleId)
        {            
            DataFilter filter = GetFilter(ModuleId);
            if (filter == null)
                return 0;
            if (filter.ItemsCount == 0)
                return 0;
            int total = 0;
            int count = 0;
            //string key = string.Format(KEY_FILTERED_NEWS, filter.FilterId);
            //List<News> list = (List<News>)DataCache.GetCache(key);
            //if (list != null)
                //return list;
            //else
            {
                int temp = 0;
                switch (GetFilter(ModuleId).FilterType)
                {
                    case FILTER.LATEST_BY_DATE:
                        total = NewsAccessHelper.GetNewsCount();
                        count = total / filter.ItemsCount;
                        if ((total % filter.ItemsCount) > 0)
                            ++count;                        
                        return --count;//to make count as zero based
                    case FILTER.LATEST_BY_CATEGORY:
                        if (Int32.TryParse(filter.Data, out temp))
                        {
                            Category category = CategoryController.GetCategory(temp);
                            if (category == null)//category does'nt exist
                                return 0;
                            total = NewsAccessHelper.GetNewsCount(category);
                            count = total / filter.ItemsCount;
                            if ((total % filter.ItemsCount) > 0)
                                ++count;
                            return --count;//to make count as zero based
                        }
                        return 0;
                    case FILTER.LATEST_BY_TAG:
                        if (Int32.TryParse(filter.Data, out temp))
                        {
                            Tag tag = TagController.GetTags().Find(p => p.TagId == temp);
                            if (tag == null)// tag does'nt exist
                                return 0;                            
                            total = NewsAccessHelper.GetNewsCount(tag);
                            count = total / filter.ItemsCount;
                            if ((total % filter.ItemsCount) > 0)
                                ++count;
                            return --count;//to make count as zero based
                        }
                        return 0;
                    case FILTER.NEWS_LIST:
                        if (string.IsNullOrEmpty(filter.Data))
                            return 0;
                        total = NewsAccessHelper.GetNewsCount(filter.Data);                        
                        count = total / filter.ItemsCount;
                        if ((total % filter.ItemsCount) > 0)
                            ++count;
                        return --count;//to make count as zero based
                }
            }
            return 0;
        }
        #endregion


        public static List<News> GetFilteredData(int ModuleId, int PageNo)
        {
            DataFilter filter = GetFilter(ModuleId);
            if (filter == null)
                return null;
            if (filter.ItemsCount == 0)
                return null;
            List<News> list = new List<News>();
            int temp = 0;
            int AccessCount = 0;
            int DisplayedCount = 0;
            switch (GetFilter(ModuleId).FilterType)
            {
                case FILTER.LATEST_BY_DATE:
                    if (GetFilteredDataPagesCount(ModuleId) > PageNo)
                    {
                        AccessCount = (filter.ItemsCount * PageNo) + filter.ItemsCount;
                        DisplayedCount = filter.ItemsCount * PageNo;
                        list = NewsAccessHelper.GetNews(AccessCount);
                        if (list.Count < AccessCount)
                            return list.GetRange(filter.ItemsCount * PageNo, list.Count - DisplayedCount);
                        return list.GetRange(filter.ItemsCount * PageNo, filter.ItemsCount);
                    }
                    return null;
                case FILTER.LATEST_BY_CATEGORY:
                    if (Int32.TryParse(filter.Data, out temp))
                    {
                        Category category = CategoryController.GetCategory(temp);
                        if (category == null)//category does'nt exist
                            return null;
                        AccessCount = (filter.ItemsCount * PageNo) + filter.ItemsCount;
                        DisplayedCount = filter.ItemsCount * PageNo;
                        list = NewsAccessHelper.GetNews(category, AccessCount);
                        if (list.Count < AccessCount)
                            return list.GetRange(filter.ItemsCount * PageNo, list.Count - DisplayedCount);
                        return list.GetRange(filter.ItemsCount * PageNo, filter.ItemsCount);
                    }
                    return null;
                case FILTER.LATEST_BY_TAG:
                    if (Int32.TryParse(filter.Data, out temp))
                    {
                        Tag tag = TagController.GetTags().Find(p => p.TagId == temp);
                        if (tag == null)// tag does'nt exist
                            return null;
                        AccessCount = (filter.ItemsCount * PageNo) + filter.ItemsCount;
                        DisplayedCount = filter.ItemsCount * PageNo;
                        list = NewsAccessHelper.GetNews(tag, AccessCount);
                        if (list.Count < AccessCount)
                            return list.GetRange(filter.ItemsCount * PageNo, list.Count - DisplayedCount);
                        return list.GetRange(filter.ItemsCount * PageNo, filter.ItemsCount);
                    }
                    return null;
                case FILTER.NEWS_LIST:
                    if (string.IsNullOrEmpty(filter.Data))
                        return null;
                    AccessCount = (filter.ItemsCount * PageNo) + filter.ItemsCount;
                    DisplayedCount = filter.ItemsCount * PageNo;
                    list = NewsAccessHelper.GetNews(filter.Data, AccessCount);
                    if (list.Count < AccessCount)
                        return list.GetRange(filter.ItemsCount * PageNo, list.Count - DisplayedCount);
                    return list.GetRange(filter.ItemsCount * PageNo, filter.ItemsCount);
            }
            return null;
        }
    }
}
